package xyz.labmem.service

import cn.hutool.core.io.FileUtil
import cn.hutool.core.util.StrUtil
import org.openqa.selenium.*
import org.openqa.selenium.NoSuchElementException
import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.chrome.ChromeOptions
import org.openqa.selenium.support.ui.ExpectedConditions
import org.openqa.selenium.support.ui.WebDriverWait
import xyz.labmem.config.BrowserReptileConfig
import xyz.labmem.core.exception.LabRunException
import java.io.File
import java.nio.file.StandardCopyOption
import java.time.Duration
import java.util.*
import javax.imageio.ImageIO


/**
 * @Author lty
 * @Date 2021/9/29 16:40
 */
class DriverOperating {

    //钩子载入超时ms
    var outTime = BrowserReptileConfig.outTime

    //文件上传超时ms
    var uploadOutTime = BrowserReptileConfig.uploadOutTime

    //线程等待时间
    var threadSleep = BrowserReptileConfig.threadSleep

    var driver: WebDriver

    init {
        val chromeOptions = ChromeOptions()
        if (!BrowserReptileConfig.showImg) {
            val images = HashMap<String, Any>()
            images["images"] = 2
            val prefs = HashMap<String, Any>()
            prefs["profile.default_content_setting_values"] = images

            chromeOptions.setExperimentalOption("prefs", prefs)
        }
        if (BrowserReptileConfig.winMax) {
            chromeOptions.addArguments("--start-maximized")
        }
        if (System.getProperty("os.name").lowercase(Locale.getDefault()).indexOf("linux") >= 0) {
            chromeOptions.addArguments("--no-sandbox")//linux 专属
        }
        if (BrowserReptileConfig.chromePath != null) {
            chromeOptions.setBinary(BrowserReptileConfig.chromePath)
        }
        driver = ChromeDriver(chromeOptions)
    }

    /**
     * 销毁WebDriver
     */
    fun driverEnd() {
        driver.close()
        driver.quit()
    }

    /**
     * 根据id获取元素
     */
    @Throws(LabRunException::class)
    fun getElementById(id: String, element: WebElement?): WebElement {
        try {
            return if (element == null)
                driver.findElement(By.xpath("""//*[@id="$id"]"""))
            else
                element.findElement(By.id(id))
        } catch (e: NoSuchElementException) {
            throw LabRunException("根据id[$id]未找到对应的元素！", e)
        } catch (e: Exception) {
            e.printStackTrace()
            throw LabRunException(e.message, e)
        }
    }

    /**
     * 根据name获取元素组
     */
    @Throws(LabRunException::class)
    fun getElementByName(name: String, element: WebElement?): MutableList<WebElement> {
        try {
            return element?.findElements(By.name(name)) ?: driver.findElements(By.name(name))
        } catch (e: NoSuchElementException) {
            throw LabRunException("根据name[$name]未找到对应的元素！", e)
        } catch (e: Exception) {
            e.printStackTrace()
            throw LabRunException(e.message, e)
        }
    }

    /**
     * 根据class获取元素组
     */
    @Throws(LabRunException::class)
    fun getElementByClass(className: String, element: WebElement?): MutableList<WebElement> {
        try {
            return if (element != null) {
                element.findElements(By.cssSelector("[class='$className']"))
            } else
                driver.findElements(By.cssSelector("[class='$className']"))
        } catch (e: NoSuchElementException) {
            throw LabRunException("根据class[$className]未找到对应的元素！", e)
        } catch (e: Exception) {
            e.printStackTrace()
            throw LabRunException(e.message, e)
        }
    }

    /**
     * 根据xpath获取元素组
     */
    @Throws(LabRunException::class)
    fun getElementByXpath(xpath: String, element: WebElement?): WebElement {
        try {
            return if (element != null) {
                element.findElement(By.cssSelector(xpath))
            } else
                driver.findElement(By.xpath(xpath))
        } catch (e: NoSuchElementException) {
            throw LabRunException("根据xpath[$xpath]未找到对应的元素！", e)
        } catch (e: Exception) {
            e.printStackTrace()
            throw LabRunException(e.message, e)
        }
    }

    /**
     * 根据元素标签获取元素组
     */
    @Throws(LabRunException::class)
    fun getElementByTag(tag: String, element: WebElement?): MutableList<WebElement> {
        try {
            return if (element != null) {
                element.findElements(By.tagName(tag))
            } else
                driver.findElements(By.tagName(tag))
        } catch (e: NoSuchElementException) {
            throw LabRunException("根据标签[$tag]未找到对应的元素！", e)
        } catch (e: Exception) {
            e.printStackTrace()
            throw LabRunException(e.message, e)
        }
    }

    /**
     * 写入值
     */
    @Throws(LabRunException::class)
    fun inputValue(element: WebElement, data: String) {
        if (StrUtil.isNotBlank(data)) {
            try {
                element.sendKeys(data)
            } catch (e: Exception) {
                throw LabRunException("数据：【$data】输入失败,请检查数据或检查模板", e)
            }
        }
    }

    /**
     * 获取text值
     */
    @Throws(LabRunException::class)
    fun getElementText(element: WebElement, fieName: String?): String {
        try {
            return element.text
        } catch (e: Exception) {
            e.printStackTrace()
            throw LabRunException("[$fieName]取值失败！", e)
        }
    }

    /**
     * 获取attribute属性值
     */
    @Throws(LabRunException::class)
    fun getElementAttribute(element: WebElement, attribute: String, fieName: String?): String {
        try {
            return element.getAttribute(attribute)
        } catch (e: Exception) {
            e.printStackTrace()
            throw LabRunException("$fieName:attribute=[$attribute]取值失败！", e)
        }
    }

    /**
     * 保存图片
     */
    @Throws(LabRunException::class)
    fun savePhoto(element: WebElement, path: String) {
        try {
            val screenshot = (driver as TakesScreenshot).getScreenshotAs(OutputType.FILE)
            val fullImg = ImageIO.read(screenshot)
            val point = element.location
            val eleScreenshot =
                fullImg.getSubimage(point.getX(), point.getY(), element.size.getWidth(), element.size.height)
            ImageIO.write(eleScreenshot, "png", screenshot)
            val screenshotLocation = File(path)
            FileUtil.copyFile(screenshot, screenshotLocation, StandardCopyOption.REPLACE_EXISTING)
        } catch (e: Exception) {
            e.printStackTrace()
            throw LabRunException("保存图片失败！")
        }
    }

    /**
     * 设置钩子等待页面加载完毕
     *
     * @param className
     * @throws Exception
     */
    @Throws(LabRunException::class)
    fun loadingFindFlagByClass(className: String, element: WebElement?) {
        try {
            Thread.sleep(threadSleep)
            if (getElementByClass(className, element).size == 0)
                throw LabRunException("页面加载超时，请稍等再试 1、检查钩子是否正确 2、网络是否缓慢？调整超时时间")
        } catch (e: Exception) {
            try {
                val wait = WebDriverWait(driver, Duration.ofMillis(outTime))
                wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".$className")))
                if (getElementByClass(className, element).size == 0)
                    throw LabRunException("页面加载超时，请稍等再试 1、检查钩子是否正确 2、网络是否缓慢？调整超时时间")
            } catch (e: Exception) {
                throw LabRunException("页面加载超时，请稍等再试 1、检查钩子是否正确 2、网络是否缓慢？调整超时时间")
            }
        }
    }

    /**
     * 设置钩子等待页面加载完毕
     *
     * @param className
     * @throws Exception
     */
    @Throws(LabRunException::class)
    fun loadingFindFlagById(id: String, element: WebElement?) {
        try {
            Thread.sleep(threadSleep)
            getElementById(id, element)
        } catch (e: Exception) {
            try {
                val wait = WebDriverWait(driver, Duration.ofMillis(outTime))
                wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("""//*[@id="$id"]""")))
                getElementById(id, element)
            } catch (e: Exception) {
                throw LabRunException("页面加载超时，请稍等再试 1、检查钩子是否正确 2、网络是否缓慢？调整超时时间")
            }
        }
    }

    /**
     * 设置钩子等待页面加载完毕
     *
     * @param className
     * @throws Exception+
     */
    @Throws(LabRunException::class)
    fun loadingFindFlagByXpath(path: String, element: WebElement?) {
        try {
            Thread.sleep(threadSleep)
            getElementByXpath(path, element)
        } catch (e: Exception) {
            try {
                val wait = WebDriverWait(driver, Duration.ofMillis(outTime))
                wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(path)))
                getElementByXpath(path, element)
            } catch (e: Exception) {
                throw LabRunException("页面加载超时，请稍等再试 1、检查钩子是否正确 2、网络是否缓慢？调整超时时间")
            }
        }
    }
}